namespace Radar.Test
{
    using System;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    using NUnit.Framework;

    [TestFixture]
    public abstract class ExceptionTestBase<T> where T : Exception
    {
        private readonly IFormatter formatter = new BinaryFormatter();

        [Test]
        public void WillConstructWithoutParameters()
        {
            var e = (Exception)Activator.CreateInstance(typeof(T));

            Assert.That(e, Is.Not.Null);
            Assert.That(e.Message, Is.EqualTo(string.Format("Exception of type '{0}' was thrown.", typeof(T))));
            Assert.That(e.InnerException, Is.Null);
        }

        [Test]
        public void WillConstructWithMessage()
        {
            const string message = "msg";

            var e = (Exception)Activator.CreateInstance(typeof(T), message);

            Assert.That(e.Message, Is.EqualTo(message));
        }

        [Test]
        public void WillConstructWithInnerException()
        {
            const string message = "msg";
            var inner = new Exception();
            var e = (Exception)Activator.CreateInstance(typeof(T), message, inner);

            Assert.That(e.Message, Is.EqualTo(message));
            Assert.That(e.InnerException, Is.EqualTo(inner));
        }

        [Test]
        public void WillSerializeAndDeserialize()
        {
            const string message = "msg";

            var e = (Exception)Activator.CreateInstance(typeof(T), message);

            var data = Serialize(e);

            var e2 = Deserialize(data);

            Assert.That(e.GetType(), Is.EqualTo(e2.GetType()));
            Assert.That(e.Message, Is.EqualTo(e2.Message));
        }

        private byte[] Serialize(Exception e)
        {
            using (var stream = new MemoryStream())
            {
                formatter.Serialize(stream, e);
                return stream.ToArray();
            }
        }

        private Exception Deserialize(byte[] data)
        {
            using (var stream = new MemoryStream(data))
            {
                return (Exception)formatter.Deserialize(stream);
            }
        }
    }
}